package view.content;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;

import javax.imageio.ImageIO;

import model.Settings;
import model.contents.Content;
import model.feature.FeatureContainerDefinition;
import model.feature.FeatureDefinition;
import model.feature.WeatherItems;

import org.json.simple.JSONObject;

import util.LoggingUtils;
import view.ProportionalAreaFitter;

public class WeatherBox extends ContentBox implements ProportionalAreaFitter{

	private static final long serialVersionUID = 1L;
	private static Map<Integer, WeatherItems> featureItems;  
	private static FeatureDefinition cdefinition;
	private static FeatureContainerDefinition definition;
	private static Hashtable<Integer, BufferedImage> store;
	private static boolean hasupdate = false;
	private BufferedImage image;
	private int ImageScaleOffset = 50;
	static {
		try
		{
			cdefinition = FeatureDefinition.getInstance();
			if(cdefinition!=null)
			{
				definition = cdefinition.getFeatureWiseDefinition("WEATHER");
			}
			featureItems = WeatherItems.getInstance().getWeatherItemsList();
			System.out.println("STATIC METHOD RUNS ONCE WHEN CLASS LOADS");
		}
		catch(Exception e)
		{
			System.out.println("Exception in static block of weatherBox");
		}
		store = new Hashtable<Integer, BufferedImage>();
	}

	public static void resetImageStore() {
		store = new Hashtable<Integer, BufferedImage>();
	}

	//private int width;
	//private int height;

	public WeatherBox(Content c, int width, int height) {

		super(c,width,height);
		System.out.println(".................from weather box width = "+width);
		System.out.println(".................from weather box height = "+height);
		this.initialize(width, height);
	}

	private void initialize(int width, int height) {
		this.width = width;
		this.height = height;
		this.setDoubleBuffered(true);
		this.setOpaque(false);
		this.setLayout(new GridLayout());
		this.setPreferredSize(new Dimension(width, height));

		if(cdefinition == null)
		{
			cdefinition = FeatureDefinition.getInstance();
			if(definition == null)
			{
				definition = cdefinition.getFeatureWiseDefinition("WEATHER");
				featureItems = WeatherItems.getInstance().getWeatherItemsList();
			}
		}
		else
		{
			if(definition == null)
			{
				definition = cdefinition.getFeatureWiseDefinition("WEATHER");
				featureItems = WeatherItems.getInstance().getWeatherItemsList();
			}
		}

		if(height < ImageScaleOffset)
		{
			ImageScaleOffset = 20;
		}
		
		loadImages();

		if(store.size()>0)
		{
			if(definition != null)
			{
				Collection<Integer> array = store.keySet();
				List<Integer> keyset = new ArrayList<Integer>(array);
				Collections.sort(keyset);

				if(definition.getOrientation() == 0) // Horizontal image view
				{
					try
					{
						int WImageWidth = (int)Math.ceil(width/definition.getNumberOfDaysToPlay());
						int xaxis = 0;

						this.image = new BufferedImage((height+5)-ImageScaleOffset, height-ImageScaleOffset, BufferedImage.TYPE_INT_ARGB);

						for(int key : keyset)
						{
							int off = (WImageWidth - height)/2;
							Graphics g2 = this.image.getGraphics();
							g2.drawImage(store.get(key), xaxis + off-10, ImageScaleOffset/2, (height+5)-ImageScaleOffset, height-ImageScaleOffset, null, null);

							xaxis = xaxis + WImageWidth;
							g2.dispose();
						}
					}
					catch(Exception e)
					{
						LoggingUtils.log(Level.SEVERE, "Error in weatherBox: ",e);
						//e.printStackTrace();
					}
				}
				else if(definition.getOrientation() == 1) // vertical image view
				{
					try
					{
						int WImageHeight = (int)Math.ceil(height/definition.getNumberOfDaysToPlay());
						int yaxis = 0;

						this.image = new BufferedImage(WImageHeight-ImageScaleOffset, WImageHeight-ImageScaleOffset, BufferedImage.TYPE_INT_ARGB);

						for(int key : keyset)
						{
							Graphics g2 = this.image.getGraphics();
							g2.drawImage(store.get(key), ImageScaleOffset/2, yaxis, WImageHeight-ImageScaleOffset, WImageHeight-ImageScaleOffset, null, null);
							yaxis = yaxis + WImageHeight;
							g2.dispose();
						}
					}
					catch(Exception e)
					{
						LoggingUtils.log(Level.SEVERE, "Error in weatherBox: ",e);
					}
				}
			}
			else
			{
				System.out.println("definition object is null from weather box");
			}
		}
		else
		{
			System.out.println("Weather Store Image empty");
		}
	}


	@Override
	public void paintComponent(Graphics g) {

		int textOffset = 15;

		if(hasupdate)
		{
			try
			{
				cdefinition = FeatureDefinition.getInstance();
				if(cdefinition!=null)
				{
					definition = cdefinition.getFeatureWiseDefinition("WEATHER");
				}

				System.out.println("Update received");
				featureItems = WeatherItems.getInstance().getWeatherItemsList();
				loadImages();
				setHasupdate(false);
			}
			catch(Exception e)
			{
				LoggingUtils.log(Level.SEVERE, "Error in weatherBox: ",e);
				setHasupdate(true);
			}
		}
		else
		{
			System.out.println("Normal running");
		}

		Graphics2D g2 = (Graphics2D)g;
		//Font font = g2.getFont().deriveFont(12f);

		if(cdefinition.getTextFormatMapper().get("option") != null)
		{
			g2 = this.ApplyFormat(g2, cdefinition.getTextFormatMapper().get("option"));
		}

		System.out.println("Paint component width= "+width+" , Height = "+height);

		if(store.size()>0)
		{
			Collection<Integer> array = store.keySet();

			List<Integer> keyset = new ArrayList<Integer>(array);
			Collections.sort(keyset);

			if(definition.getOrientation() == 0) // Horizontal image view
			{
				try
				{
					int WImageWidth = (int)Math.ceil(width/definition.getNumberOfDaysToPlay());
					int textHeight = (int)Math.ceil(height/2);

					int xaxis = 0;
					for(int key : keyset)
					{
						int off = (WImageWidth - height)/2;
						g2.drawImage(store.get(key), xaxis + off-10, ImageScaleOffset/2, (height+5)-ImageScaleOffset, height-ImageScaleOffset, null, null);

						WeatherItems items = featureItems.get(key);

						if(items.getWeatherDay().trim() != null && items.getWeatherDate() != null)
						{
							String toptext = formatWeatherDay(items.getWeatherDay(),items.getWeatherDate());
							
							if(!toptext.equals(""))
								g2.drawString(toptext, xaxis + 10, textOffset);
						}

						String maxTemp = "";

						if(!items.getMaxTemp().trim().equals("") && items.getMaxTemp() != null)
						{
							//g2.drawString(items.getTextMiddle().trim(), xaxis + textOffset, textHeight);
							maxTemp = items.getMaxTemp().trim();
						}

						String weatherInfo = "";

						if(!items.getWeatherInfo().trim().equals("") && items.getWeatherInfo() != null)
						{
							weatherInfo = items.getWeatherInfo().trim();
							// String string = "\u00b0";
							//System.out.println(items.getTextBottom().trim());

							//g2.drawString(items.getTextBottom().trim(), xaxis + textOffset, height);
						}
						
						String bottomText = processTemparature(maxTemp,definition.getUnit())+weatherInfo;
						
						g2.drawString(bottomText, xaxis, height - 10);

						xaxis = xaxis + WImageWidth;
					}
				}
				catch(Exception e)
				{
					LoggingUtils.log(Level.SEVERE, "Error in weatherBox: ",e);
				}
			}
			else if(definition.getOrientation() == 1) // vertical image view
			{
				try
				{
					int WImageHeight = (int)Math.ceil(height/definition.getNumberOfDaysToPlay());
					int yaxis = 0;
					int textHeight = (int)Math.ceil(WImageHeight/2);

					for(int key : keyset)
					{
						WeatherItems items = featureItems.get(key);

						g2.drawImage(store.get(key), ImageScaleOffset/2, yaxis, WImageHeight-ImageScaleOffset, WImageHeight-ImageScaleOffset, null, null);

						
						if(items.getWeatherDay().trim() != null && items.getWeatherDate() != null)
						{
							String toptext = formatWeatherDay(items.getWeatherDay(),items.getWeatherDate());
							
							int toptext_xoffset = textOffset;
							if(width - toptext.length() > 0)
								toptext_xoffset = (width - toptext.length())/2;
							
							if(!toptext.equals(""))
								g2.drawString(toptext, toptext_xoffset , textOffset+yaxis);
						}

						String maxTemp = "";

						if(!items.getMaxTemp().trim().equals("") && items.getMaxTemp() != null)
						{
							//g2.drawString(items.getTextMiddle().trim(), textOffset, textHeight+yaxis+textOffset);
							maxTemp = items.getMaxTemp().trim();
						}

						String weatherInfo = "";

						if(!items.getWeatherInfo().trim().equals("") && items.getWeatherInfo() != null)
						{
							weatherInfo = items.getWeatherInfo().trim();
							//g2.drawString(items.getTextBottom().trim(), textOffset, (WImageHeight+yaxis+5)-textOffset);
						}
						
						String bottomText = processTemparature(maxTemp,definition.getUnit())+weatherInfo;
						
						int bottomText_xoffset = textOffset;
						if((width - bottomText.length()) > 0)
							bottomText_xoffset = (width - bottomText.length())/2;

						g2.drawString(bottomText, bottomText_xoffset, (WImageHeight+yaxis+5)-textOffset);
						yaxis = yaxis + WImageHeight;
					}
				}
				catch(Exception e)
				{
					LoggingUtils.log(Level.SEVERE, "Error in weatherBox: ",e);
				}
			}
		}
		g2.dispose();
		g.dispose();
	}

	public static void loadImages() {
		store = new Hashtable<Integer, BufferedImage>();

		if(featureItems != null)
		{
			Collection<Integer> keyset = featureItems.keySet();

			for (Integer key : keyset) {

				WeatherItems itemRecords = featureItems.get(key);

				String imagePath = Settings.IMAGE_DIRECTORY+itemRecords.getImage();
				try {
					
					File imageFile = new File(imagePath);
					
					if(!imageFile.exists())
					{
						imageFile = new File(Settings.IMAGE_DIRECTORY+"mostly_sunny.gif");
					}
					
					if(!store.containsKey(key) && imageFile.exists()) {
						store.put(key, ImageIO.read(imageFile));
					}
				} catch (IOException e) {
					// TODO Auto-generated catch block
					LoggingUtils.log(Level.SEVERE, "Error in weatherBox: ",e);
				}
				catch(Exception e)
				{
					LoggingUtils.log(Level.SEVERE, "Error in weatherBox: ",e);
				}
			}

		}
	}

	@Override
	public void fit(int maxWidth, int maxHeight) {
		System.out.println("Fitting==="+maxWidth);
		/*FeatureDefinition definition = FeatureDefinition.getInstance();

		int containerID = definition.getContainerID();
		//containers.add(Player.getInstance().getContainer(containerID));
		Container container = Player.getInstance().getContainer(containerID);
		int containerWidth = container.getWidth();
		int containerHeight = container.getHeight();*/

		int containerWidth = width;
		int containerHeight = height;

		double scale = calculateFittingScale(maxWidth, maxHeight, containerWidth, containerHeight);

		containerWidth = (int) (containerWidth * scale);
		containerHeight = (int) (containerHeight * scale);

		this.initialize(containerWidth, containerHeight);
		this.revalidate();
		this.repaint();
	}

	private double calculateFittingScale(int maxWidth, int maxHeight, int imageWidth, int imageHeight) {
		double widthRatio = (double) imageWidth / maxWidth;
		double heightRatio = (double) imageHeight / maxHeight;
		double scale = 1 / Math.max(widthRatio, heightRatio);
		return scale;
	}

	private Graphics2D ApplyFormat(Graphics2D g2, JSONObject obj)
	{
		//System.out.println("Applying format GRAPHICS 2D");

		String fontName = obj.get("font") == null ? "Arial" : obj.get("font").toString();
		String fontSize = obj.get("font-size") == null ? "14" : obj.get("font-size").toString();
		String fontColor = obj.get("color") == null ? "000000" : obj.get("color").toString();

		int style = 0;

		if (obj.get("font-style") != null) {
			style = Font.ITALIC | style;
		}
		if (obj.get("font-weight") != null) {
			style = Font.BOLD | style;
		}

		Font font = new Font(fontName, style, Integer.parseInt(fontSize));
		Color color = this.getColor(fontColor);
		g2.setFont(font);
		g2.setColor(color);
		g2.setPaint(color);
		return g2;
	}
	
	public String formatWeatherDay(String WeatherDay, String WeatherDate)
	{
		if(WeatherDate.equals("") && WeatherDay.equals(""))
			return "";
		
		if(WeatherDate.equals("") && !WeatherDay.equals(""))
			return WeatherDay.trim();
		
		if(cdefinition == null)
			return WeatherDay.trim();
		
		String format = cdefinition.getTextFormatMapper().get("option").get("dateformat") == null ? "" : cdefinition.getTextFormatMapper().get("option").get("dateformat").toString();
		
		String toptext = getFormattedDate(format.trim(),WeatherDate.trim());
		
		if(toptext.equals(""))
			return WeatherDay.trim();
		
		return toptext;	
	}
	
	private String getFormattedDate(String formatString, String serverDate)
	{
		if(formatString.equals(""))
			return "";
		if(serverDate.equals(""))
			return "";
		
		formatString = formatString.trim();
		String formattedDate = "";
		
		if(formatString.equals(""))
		{
			return formattedDate;
		}
		
		DateFormat df;
		
		try
		{
			/* Formatting the server date to java formatted date */
			DateFormat serverDateFormat = new SimpleDateFormat("yyyy-MM-dd");
			Date date = (Date)serverDateFormat.parse(serverDate);
			
			/* converting date to user specified format */
			
			df = new SimpleDateFormat(formatString);
			formattedDate = df.format(date);
		}
		catch(Exception e)
		{
			//e.printStackTrace();
			LoggingUtils.log(Level.SEVERE, "Wrong date format from server: setting the player default format weather box " + e.getMessage());
			formattedDate = "";
		}
		return formattedDate;
	}

	public static boolean isHasupdate() {
		return hasupdate;
	}

	public static void setHasupdate(boolean hasupdate) {
		WeatherBox.hasupdate = hasupdate;
	}

	private Color getColor(String colorString) {
		Color color = new Color(Integer.parseInt(colorString.substring(0, 2), 16), Integer.parseInt(
				colorString.substring(2, 4), 16), Integer.parseInt(colorString.substring(4, 6), 16));

		return color;
	}

	private String processTemparature(String Tf, String unit)
	{
		Tf = Tf.trim().equals("") ? "0" : Tf.trim();
		
		if(unit.equals("F"))
		{
			return Tf+"\u00b0F ";
		}
		else if(unit.equals("C"))
		{
			int fT = Integer.parseInt(Tf);
			int celcius = ((fT-32)*5)/9;
			return celcius+"\u00b0C ";
		}
		else
		{
			return Tf+"\u00b0F ";
		}
	}
}